/*
 * JuliaSet.java
 */

package complexchaos;

import java.util.ArrayList;

/**
 * This class is designed to compute the filled Julia set of a complex valued
 * function. It will then determine which points in the given region are in the
 * set based on a given seed. The algorithm used is given as:
 *
 * Choose a maximum number of iterations, N. For each point z in the grid,
 * compute the first N points on the orbit of z. If |F^i(z)| > max{|r|, 2}
 * for some i <= N, then stop iterating and color z black. If
 * |F^i(z)| <= max{|r|, 2} for all i <= N, then color z white. White points
 * have orbits that escape, wheas black points do not, at least for the
 * first N iterations. So the white points yield an approximation to the
 * Julia set.
 *
 * This is from the algorith from "A First Course In Chaotic Dynamical Systems"
 * by Robert L. Devaney.
 *
 * @author Dan Kotowski
 */
public class JuliaSet {
    private ComplexNumber lowerBound, upperBound;
    private ExpFormula F;
    private int maxIter, renderHeight, renderWidth;
    private double escapeRadius;
    private ArrayList filledSet;

    /**
     * Class constructor.
     * @param lowerComplexBound the lower left bound for the complex region
     * @param upperComplexBound the upper right bound for the complex region
     * @param FofZ copmlex valued function to compute using
     * @param maxIteration the maximum number of iterations to use
     * @param r maximum escape radius
     * @param h the height of the panel to render the set in
     * @param w the width of the panel to render the set in
     */
    public JuliaSet (ComplexNumber lowerComplexBound, ComplexNumber upperComplexBound, ExpFormula FofZ, int maxIteration, double r, int h, int w)
    {
        // initialize everything
        lowerBound = lowerComplexBound.clone();
        upperBound = upperComplexBound.clone();
        F = FofZ.clone();
        maxIter = maxIteration;
        renderHeight = h;
        renderWidth = w;
        escapeRadius = r;
        computeFilledJuliaSet();
    }

    /**
     * Checks whether a single point is in the filled set.
     * @param z complex number to check
     * @return whether or not the point is in the set
     */
    public boolean inSet(ComplexNumber z)
    {
        int iter = 1;
        boolean zInSet = false;
        String zString = new String(z.toString());
        ExpFormula zForm = new ExpFormula(zString);
        
        VarValList VVz = new VarValList("z", zForm);
        F = F.evaluate(VVz.getList());
        while (iter <= maxIter && zForm.getNumber().mod() < escapeRadius) {
            VarValList tempVVList = new VarValList("z", F);
            F = F.evaluate(tempVVList.getList());
            iter++;
        }
        if (iter == maxIter) zInSet = true;
        else zInSet = false;
        return zInSet;
    }

    /**
     * Computes the filled Julia set.
     */
    public void computeFilledJuliaSet()
    {
        filledSet = new ArrayList();
        double deltaR = Math.abs(upperBound.getReal().approx() - lowerBound.getReal().approx()) / renderWidth;
        double deltaI = Math.abs(upperBound.getImag().approx() - lowerBound.getImag().approx()) / renderHeight;

        for (double i = lowerBound.getReal().approx(); i < upperBound.getReal().approx(); i += deltaR)
        {
            for (double j = lowerBound.getImag().approx(); j < upperBound.getImag().approx(); j += deltaI)
            {
                // calculate some crap
                ComplexNumber currentZ = new ComplexNumber(i, j);
                if (this.inSet(currentZ)) filledSet.add(currentZ);
            }
        }
    }

    /**
     * Allows things external to the class to read the set
     * @return the set of complex numbers in the filled Julia set
     */
    public ArrayList getFilledSet() {
        return filledSet;
    }
}
